home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 0.9.1.3 stable / flock-0.9.1.3.en-US.win32.exe / flock / components / flockPollerService.js < prev    next >
Text File  |  2007-10-12  |  15KB  |  518 lines

  1. // BEGIN FLOCK GPL
  2. // 
  3. // Copyright Flock Inc. 2005-2007
  4. // http://flock.com
  5. // 
  6. // This file may be used under the terms of of the
  7. // GNU General Public License Version 2 or later (the "GPL"),
  8. // http://www.gnu.org/licenses/gpl.html
  9. // 
  10. // Software distributed under the License is distributed on an "AS IS" basis,
  11. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. // for the specific language governing rights and limitations under the
  13. // License.
  14. // 
  15. // END FLOCK GPL
  16. //
  17.  
  18. const PS_CONTRACTID = '@flock.com/poller-service;1';
  19. const PS_CLASSID    = Components.ID('{a2b4cd0e-804b-4578-8c3c-4b61f8c1047a}');
  20. const PS_CLASSNAME  = 'Flock Poller';
  21.  
  22.  
  23. const PREF_POLLER_BRANCH            = 'flock.poller'
  24. const PREF_REFRESH_DELAY            = '.refreshDelay';
  25. const PREF_REFRESH_TIMEOUT          = '.refreshTimeout';
  26. const PREF_DEFAULT_REFRESH_INTERVAL = '.defaultRefreshInterval';
  27. const PREF_MAX_CONCURRENT_REFRESHES = '.maxConcurrentRefreshes';
  28.  
  29. // According to Mozilla docs, timers can't be set for more than approximately
  30. // 6 hours. We'll limit to 4 hours just to be conservative.
  31. const MAX_TIMER_INTERVAL = 4 * 60 * 60 * 1000;
  32.  
  33.  
  34. const Ci = Components.interfaces;
  35. const Cc = Components.classes;
  36. const Cr = Components.results;
  37.  
  38.  
  39. function getObserverService() {
  40.   return Cc['@mozilla.org/observer-service;1']
  41.     .getService(Ci.nsIObserverService);
  42.  
  43.  
  44. function RefreshListener(poller, urn, serviceId) {
  45.   this._poller = poller;
  46.   this._urn = urn;
  47.   this._serviceId = serviceId;
  48. }
  49.  
  50. RefreshListener.prototype = {
  51.   onResult: function PSL_onResult() { 
  52.     this._poller._logger.info('Successful refresh for ' + this._urn);
  53.     this._poller._finishedRefresh(this._urn);
  54.   },
  55.   onError: function PSL_onError(error) {
  56.     this._poller._logger.error('The service ' + this._serviceId + ' returned an ' +
  57.                                'error while refreshing ' + this._urn);
  58.     this._poller._finishedRefresh(this._urn);
  59.   },
  60.   notify: function PSL_notify(timer) {
  61.     this._poller._logger.error('The service ' + this._serviceId + ' timed out ' +
  62.                                'while refreshing ' + this._urn);
  63.     this._poller._finishedRefresh(this._urn);
  64.   },
  65.   QueryInterface: function PSL_QueryInterface(iid) {
  66.     if (iid.equals(Ci.flockIPollerListener) ||
  67.         iid.equals(Ci.nsITimerCallback) ||
  68.         iid.equals(Ci.nsISupports))
  69.       return this;
  70.     throw Cr.NS_ERROR_NO_INTERFACE;
  71.   }
  72. }
  73.  
  74.  
  75. function PollerService() {
  76.   var obs = getObserverService();
  77.   obs.addObserver(this, 'flock-data-ready', false);
  78.   obs.addObserver(this, 'xpcom-shutdown', false);
  79. }
  80.  
  81. PollerService.prototype = {
  82.   _start: function PS__start() {
  83.     this._logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
  84.     this._logger.init('poller');
  85.     this._logger.info('starting up...');
  86.  
  87.     this._profiler = Cc["@flock.com/profiler;1"].getService(Ci.flockIProfiler);
  88.     var evtID = this._profiler.profileEventStart('poller-start');
  89.  
  90.     this._timer = Cc['@mozilla.org/timer;1'].createInstance(Ci.nsITimer);
  91.  
  92.     this._coop = Cc['@flock.com/singleton;1'].getService(Ci.flockISingleton)
  93.       .getSingleton('chrome://browser/content/flock/common/load-faves-coop.js')
  94.       .wrappedJSObject;
  95.  
  96.     var prefService = Cc['@mozilla.org/preferences-service;1']
  97.       .getService(Ci.nsIPrefBranch2);
  98.     prefService.addObserver(PREF_POLLER_BRANCH, this, false);
  99.  
  100.     this.observe(null, 'nsPref:changed', PREF_REFRESH_DELAY);
  101.     this.observe(null, 'nsPref:changed', PREF_REFRESH_TIMEOUT);
  102.     this.observe(null, 'nsPref:changed', PREF_DEFAULT_REFRESH_INTERVAL);
  103.     this.observe(null, 'nsPref:changed', PREF_MAX_CONCURRENT_REFRESHES);
  104.  
  105.     this._initializeQueue();
  106.     this._watchRDFEvents();
  107.  
  108.     this._profiler.profileEventEnd(evtID, "");
  109.   },
  110.   _shutdown: function PS__shutdown() {
  111.     var prefService = Cc['@mozilla.org/preferences-service;1']
  112.       .getService(Ci.nsIPrefBranch2);
  113.     prefService.removeObserver(PREF_POLLER_BRANCH, this);
  114.  
  115.     this._timer.cancel();
  116.     this._timer = null;
  117.   },
  118.  
  119.   _prefChanged: function PS__prefChanged(pref) {
  120.     var prefService = Cc['@mozilla.org/preferences-service;1']
  121.       .getService(Ci.nsIPrefService);
  122.     var prefBranch = prefService.getBranch(PREF_POLLER_BRANCH);
  123.  
  124.     switch (pref) {
  125.       case PREF_REFRESH_DELAY:
  126.         this._refreshDelay = prefBranch.getIntPref(pref);
  127.         break;
  128.  
  129.       case PREF_REFRESH_TIMEOUT:
  130.         this._refreshTimeout = prefBranch.getIntPref(pref);
  131.         break;
  132.  
  133.       case PREF_DEFAULT_REFRESH_INTERVAL:
  134.         this._defaultRefreshInterval = prefBranch.getIntPref(pref);
  135.         break;
  136.  
  137.       case PREF_MAX_CONCURRENT_REFRESHES:
  138.         this._maxConcurrentRefreshes = prefBranch.getIntPref(pref);
  139.         break;
  140.     }
  141.   },
  142.  
  143.   _watchRDFEvents: function PS__watchRDFEvents() {
  144.     var RDFS = Cc['@mozilla.org/rdf/rdf-service;1']
  145.       .getService(Ci.nsIRDFService);
  146.     var nextRefresh = RDFS.GetResource('http://flock.com/rdf#nextRefresh');
  147.     var isPollable = RDFS.GetResource('http://flock.com/rdf#isPollable');
  148.  
  149.     var faves = Cc['@mozilla.org/rdf/datasource;1?name=flock-favorites']
  150.       .getService(Ci.flockIRDFObservable);
  151.     faves.addArcObserver(Ci.flockIRDFObserver.TYPE_ALL, null, nextRefresh, null,
  152.                          this);
  153.     faves.addArcObserver(Ci.flockIRDFObserver.TYPE_ALL, null, isPollable, null,
  154.                          this);
  155.   },
  156.   rdfChanged: function PS_rdfChanged(ds, type, rsrc, pred, obj, oldObj) {
  157.     var pollable = this._coop.get_from_resource(rsrc);
  158.  
  159.     if (pollable && pollable.isPollable) {
  160.       if (pollable.nextRefresh)
  161.         this._addToQueue(pollable);
  162.       else
  163.         pollable.nextRefresh = new Date();
  164.     } else {
  165.       rsrc.QueryInterface(Ci.nsIRDFResource);
  166.       this._removeFromQueue(rsrc.ValueUTF8);
  167.     }
  168.   },
  169.  
  170.   _initializeQueue: function PS__initializeQueue() {
  171.     var queue = [];
  172.     var dates = {};
  173.  
  174.     var RDFS = Cc['@mozilla.org/rdf/rdf-service;1']
  175.       .getService(Ci.nsIRDFService);
  176.     var isPollable = RDFS.GetResource('http://flock.com/rdf#isPollable');
  177.  
  178.     var faves = Cc['@mozilla.org/rdf/datasource;1?name=flock-favorites']
  179.       .getService(Ci.nsIRDFDataSource);
  180.  
  181.     var pollables = faves.GetSources(isPollable, RDFS.GetLiteral('true'), true);
  182.     while (pollables.hasMoreElements()) {
  183.       var pollable = this._coop.get_from_resource(pollables.getNext());
  184.  
  185.       var urn = pollable.id();
  186.       queue.push(urn);
  187.  
  188.       if (!pollable.nextRefresh)
  189.         pollable.nextRefresh = new Date();
  190.  
  191.       dates[urn] = pollable.nextRefresh;
  192.     }
  193.  
  194.     function sorter(a, b) {
  195.       return dates[a] - dates[b];
  196.     }
  197.     queue.sort(sorter);
  198.  
  199.     this._queue = queue;
  200.     this._dates = dates;
  201.  
  202.     this._refreshing = {};
  203.     this._refreshOrder = [];
  204.  
  205.     this._lastRefresh = new Date(0);
  206.  
  207.     this._recalculateTimer();
  208.     this._logger.debug('queue initialized: ' + this._queue.toSource());
  209.   },
  210.  
  211.   _addToQueue: function PS__addToQueue(obj) {
  212.     var urn = obj.id();
  213.     var nextRefresh = obj.nextRefresh;
  214.  
  215.     var date = this._dates[urn];
  216.     if (date) {
  217.       if (date - nextRefresh == 0)
  218.         return;
  219.       else
  220.         this._queue.splice(this._queue.indexOf(urn), 1);
  221.     }
  222.  
  223.     this._dates[urn] = nextRefresh;
  224.  
  225.     for (var i = 0; i < this._queue.length; i++) {
  226.       var date = this._dates[this._queue[i]];
  227.       if (nextRefresh < date) {
  228.         this._queue.splice(i, 0, urn);
  229.         if (i == 0)
  230.           this._recalculateTimer();
  231.  
  232.         this._logger.debug('queue updated: ' + this._queue.toSource());
  233.         return;
  234.       }
  235.     }
  236.  
  237.     this._queue.push(urn);
  238.     if (this._queue.length == 1)
  239.       this._recalculateTimer();
  240.  
  241.     this._logger.debug('queue updated: ' + this._queue.toSource());
  242.   },
  243.  
  244.   _removeFromQueue: function PS__removeFromQueue(urn) {
  245.     var date = this._dates[urn];
  246.     if (!date)
  247.       return;
  248.  
  249.     delete this._dates[urn];
  250.  
  251.     var index = this._queue.indexOf(urn);
  252.     if (index == 0) {
  253.       this._queue.shift();
  254.       this._recalculateTimer();
  255.     } else {
  256.       this._queue.splice(index, 1);
  257.     }
  258.  
  259.     this._logger.debug('queue updated: ' + this._queue.toSource());
  260.   },
  261.  
  262.   _recalculateTimer: function PS__recalculateTimer() {
  263.     this._timer.cancel();
  264.  
  265.     if (this._queue.length) {
  266.       var date = this._dates[this._queue[0]];
  267.       var now = new Date();
  268.  
  269.       var interval = date > now ? date - now : 0;
  270.       interval = Math.min(interval, MAX_TIMER_INTERVAL);
  271.  
  272.       if (this._refreshOrder.length) {
  273.         var urn = this._refreshOrder[this._refreshOrder.length - 1];
  274.         var start = this._refreshing[urn].start;
  275.         var delay = (this._refreshDelay * 1000) - (now - start);
  276.         interval = Math.max(interval, delay);
  277.  
  278.         if (this._refreshOrder.length > this._maxConcurrentRefreshes - 1) {
  279.           urn = this._refreshOrder[0];
  280.           start = this._refreshing[urn].start;
  281.           delay = (this._refreshTimeout * 1000) - (now - start);
  282.           interval = Math.max(interval, delay);
  283.         }
  284.       } else {
  285.         var delay = (this._refreshDelay * 1000) - (now - this._lastRefresh);
  286.         interval = Math.max(interval, delay);
  287.       }
  288.  
  289.       this._logger.debug('next refresh: ' + interval / 1000);
  290.       this._timer.initWithCallback(this, interval, Ci.nsITimer.TYPE_ONE_SHOT);
  291.     }
  292.   },
  293.  
  294.   _finishedRefresh: function PS__finishedRefresh(urn) {
  295.     var index = this._refreshOrder.indexOf(urn);
  296.     if (index == -1)
  297.       return;
  298.  
  299.     var refreshInfo = this._refreshing[urn]
  300.     refreshInfo.timer.cancel();
  301.  
  302.     delete this._refreshing[urn];
  303.     this._refreshOrder.splice(index, 1);
  304.  
  305.     var obj = this._coop.get(urn);
  306.     if (!obj)
  307.       return;
  308.  
  309.     var now = new Date();
  310.     if (obj.nextRefresh < now) {
  311.       var refreshInterval = obj.refreshInterval ? obj.refreshInterval
  312.                                                 : this._defaultRefreshInterval;
  313.  
  314.       obj.nextRefresh = new Date(now.getTime() + refreshInterval * 1000);
  315.     }
  316.  
  317.     this._recalculateTimer();
  318.  
  319.     var obs = getObserverService();
  320.     obs.notifyObservers(null, "refresh-myworld", null);
  321.   },
  322.   _refresh: function PS__refresh(obj) {
  323.     var urn = obj.id();
  324.     if (this._refreshing[urn])
  325.       return;
  326.  
  327.     var serviceId = obj.serviceId;
  328.     this._logger.debug('serviceId = ' + serviceId);
  329.  
  330.     var service = null;
  331.     if (serviceId && Cc[serviceId]) {
  332.       try {
  333.         service = Cc[serviceId].getService(Ci.flockIPollingService);
  334.       }
  335.       catch (e) {
  336.         this._logger.error('Problem getting "' + serviceId + '" as a ' +
  337.                            'flockIPollingService, while trying to refresh ' + 
  338.                            urn);
  339.       }
  340.     }
  341.  
  342.     if (service) {
  343.       this._logger.info('Refreshing ' + urn);
  344.       try {
  345.         var now = new Date();
  346.  
  347.         var listener = new RefreshListener(this, urn, serviceId);
  348.         service.refresh(urn, listener);
  349.  
  350.         var timer = Cc['@mozilla.org/timer;1'].createInstance(Ci.nsITimer);
  351.         timer.initWithCallback(listener, this._refreshTimeout * 1000,
  352.                                Ci.nsITimer.TYPE_ONE_SHOT);
  353.  
  354.         this._refreshing[urn] = { start: now, timer: timer };
  355.         this._refreshOrder.push(urn);
  356.  
  357.         this._lastRefresh = now;
  358.       }
  359.       catch (e) {
  360.         this._logger.error('Exception while ' + serviceId + ' was refreshing ' +
  361.                            'a stream: ' + e);
  362.       }
  363.     } else {
  364.       this._logger.error('Problem getting the service, while trying to ' +
  365.                          'refresh ' + urn);
  366.     }
  367.   },
  368.  
  369.   notify: function PS_notify(timer) {
  370.     var urn = this._queue[0];
  371.     var date = this._dates[urn];
  372.  
  373.     var now = new Date();
  374.     if (now < date) {
  375.       this._recalculateTimer();
  376.       return;
  377.     }
  378.  
  379.     this._queue.shift();  
  380.     delete this._dates[urn];
  381.  
  382.     var obj = this._coop.get(urn);
  383.     if (obj)
  384.       this._refresh(this._coop.get(urn));
  385.     else
  386.       this._logger.warn('trying to refresh nonexistent object: ' + urn);
  387.  
  388.     this._recalculateTimer();
  389.   },
  390.  
  391.   observe: function PS_observe(subject, topic, state) {
  392.     var obs = getObserverService();
  393.  
  394.     switch (topic) {
  395.       case 'flock-data-ready':
  396.         obs.removeObserver(this, 'flock-data-ready');
  397.         this._start();
  398.         break;
  399.  
  400.       case 'xpcom-shutdown':
  401.         obs.removeObserver(this, 'xpcom-shutdown');
  402.         this._shutdown();
  403.         break;
  404.  
  405.       case 'nsPref:changed':
  406.         this._prefChanged(state);
  407.         break;
  408.     }
  409.   },
  410.  
  411.   forceRefresh: function PS_forceRefresh(urn) {
  412.     var obj = this._coop.get(urn);
  413.     if (!obj)
  414.       throw 'URN ' + urn + ' does not exist'; 
  415.  
  416.     obj.nextRefresh = new Date(0);
  417.   },
  418.  
  419.   getInterfaces: function PS_getInterfaces(countRef) {
  420.     var interfaces = [Ci.flockIPollerService, Ci.flockIRDFObserver,
  421.                       Ci.nsITimerCallback, Ci.nsIObserver, Ci.nsIClassInfo,
  422.                       Ci.nsISupports];
  423.     countRef.value = interfaces.length;
  424.     return interfaces;
  425.   },
  426.   getHelperForLanguage: function PS_getHelperForLanguage(language) {
  427.     return null;
  428.   },
  429.   contractID: PS_CONTRACTID,
  430.   classDescription: PS_CLASSNAME,
  431.   classID: PS_CLASSID,
  432.   implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
  433.   flags: Ci.nsIClassInfo.SINGLETON,
  434.  
  435.   QueryInterface: function PS_QueryInterface(iid) {
  436.     if (iid.equals(Ci.flockIPollerService) ||
  437.         iid.equals(Ci.flockIRDFObserver) ||
  438.         iid.equals(Ci.nsITimerCallback) ||
  439.         iid.equals(Ci.nsIObserver) ||
  440.         iid.equals(Ci.nsIClassInfo) ||
  441.         iid.equals(Ci.nsISupports))
  442.       return this;
  443.     throw Cr.NS_ERROR_NO_INTERFACE;
  444.   }
  445. }
  446.  
  447.  
  448. function GenericComponentFactory(ctor) {
  449.   this._ctor = ctor;
  450. }
  451.  
  452. GenericComponentFactory.prototype = {
  453.  
  454.   _ctor: null,
  455.  
  456.   // nsIFactory
  457.   createInstance: function(outer, iid) {
  458.     if (outer != null)
  459.       throw Cr.NS_ERROR_NO_AGGREGATION;
  460.     return (new this._ctor()).QueryInterface(iid);
  461.   },
  462.  
  463.   // nsISupports
  464.   QueryInterface: function(iid) {
  465.     if (iid.equals(Ci.nsIFactory) ||
  466.         iid.equals(Ci.nsISupports))
  467.       return this;
  468.     throw Cr.NS_ERROR_NO_INTERFACE;
  469.   },
  470. };
  471.  
  472. var Module = {
  473.   QueryInterface: function(iid) {
  474.     if (iid.equals(Ci.nsIModule) ||
  475.         iid.equals(Ci.nsISupports))
  476.       return this;
  477.  
  478.     throw Cr.NS_ERROR_NO_INTERFACE;
  479.   },
  480.  
  481.   getClassObject: function(cm, cid, iid) {
  482.     if (!iid.equals(Ci.nsIFactory))
  483.       throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  484.  
  485.     if (cid.equals(PS_CLASSID))
  486.       return new GenericComponentFactory(PollerService);
  487.  
  488.     throw Cr.NS_ERROR_NO_INTERFACE;
  489.   },
  490.  
  491.   registerSelf: function(cm, file, location, type) {
  492.     var cr = cm.QueryInterface(Ci.nsIComponentRegistrar);
  493.     cr.registerFactoryLocation(PS_CLASSID, PS_CLASSNAME, PS_CONTRACTID,
  494.                                file, location, type);
  495.  
  496.     var catman = Cc['@mozilla.org/categorymanager;1']
  497.       .getService(Ci.nsICategoryManager);
  498.     catman.addCategoryEntry('flock-startup', PS_CLASSNAME,
  499.                             'service,' + PS_CONTRACTID,
  500.                             true, true);
  501.   },
  502.  
  503.   unregisterSelf: function(cm, location, type) {
  504.     var cr = cm.QueryInterface(Ci.nsIComponentRegistrar);
  505.     cr.unregisterFactoryLocation(PS_CLASSID, location);
  506.   },
  507.  
  508.   canUnload: function(cm) {
  509.     return true;
  510.   },
  511. };
  512.  
  513. function NSGetModule(compMgr, fileSpec)
  514. {
  515.   return Module;
  516. }
  517.